home *** CD-ROM | disk | FTP | other *** search
- /*
- bldfuncs - contruct a table of the functions defined in C
- source files.
-
- Usage: bldfuncs file1.c file2.c ...
- (wildcards are allowed also, e.g. *.c )
-
- The output table is named funcs.txt
-
-
- Copyright (c) 1988 Marvin Hymowech
- Reference: DDJ, Aug 88
-
- */
-
- #include <stdio.h>
- #include <string.h>
-
-
- #define LINT_ARGS
- #define PRINTING_LINE_NUMBER
-
- #define TRUE 1
- #define FALSE 0
- #define OK 0
- #define ERROR 1
-
- /*
- return values for filter functions below.
- EOF or any character is also possible.
- */
- #define BRACES -2
- #define PARENS -3
- #define QUOTES -4
-
- int line_num; /* line number in input file */
-
- /* function prototypes for type checking */
- char *get_fn_name( char * );
- int get_names_one_file( char *, FILE * );
- int filter_data( FILE * );
- int filter_parens( FILE * );
- int filter_curly_braces( FILE * );
- int filter_ppdir( FILE * );
- int filter_quotes( FILE * );
- int filter_cmt( FILE * );
-
- /* nonzero if new data is to be appended to existing file */
- int appending = 0;
-
- main(argc, argv) int argc; char **argv;
- {
- FILE *fp_out;
- char *current_file, **av, *cp, c;
- int num_files, i, ac;
-
- quotearg(&argc, &argv); /* expand wildcards on command line */
-
- /* parse option switches on command line & remove them */
- ac = argc; argc = 1;
- av = argv + 1;
- while(--ac)
- {if(**av == '-')
- {cp = *av + 1;
- while(c = *cp++)
- {switch(c)
- {case 'a': appending = 1; break;
- default: help();
- }
- }
- }
- else
- argv[argc++] = *av;
- av++;
- }
-
- if(argc < 2) help();
- if( (fp_out = fopen("funcs.txt", appending?"a":"w")) == NULL )
- {
- fprintf( stderr, "can't open %s\n", "funcs.txt" );
- exit(1);
- }
- /*
- build a function list for each file on the command line,
- and write the list to the file funcs.txt.
- */
- printf( "%s funcs.txt...\n", appending?"Appending to":"Creating" );
- num_files = argc - 1;
- for ( i=1; i<=num_files; i++)
- { /* tell the user where we're at */
- printf( "%30s: %3d of %3d files\n",
- current_file=strlwr(*++argv), i, num_files);
- if( get_names_one_file( current_file, fp_out ) != OK )
- { /* use strlwr to lower_case the name - cosmetic only */
- fprintf( stderr, "can't process %s", current_file );
- exit(1);
- }
- }
- fclose(fp_out);
- exit(0);
- }
-
- /*
- open the .c file source_file_name, scan it for function definitions,
- and write a listing to fp_out in the form:
-
- source_file_name:
- function-1
- function-2
- ...
- function-n;
- return values: OK or ERROR
- */
- int
- get_names_one_file( source_file_name, fp_out )
- char *source_file_name;
- FILE *fp_out;
- {
- int line_len, c, got_fn_defn=FALSE, definition_line_num;
- #define LINE_LEN 8192
- char line[LINE_LEN], *name_ptr, fn_name[64];
- FILE *fp_source;
- /* open the input source file */
- if( (fp_source = fopen(source_file_name, "r")) == NULL )
- return ERROR;
-
- /* write "source file name:" */
- sprintf( line, "\n%s:", source_file_name );
- fprintf( fp_out, line );
-
- line_num=1;
-
- while( TRUE )
- {
- line_len = 0; /* using the filter_data() char stream */
- /* collect chars until a semicolon or PARENS */
- while( (c = filter_data(fp_source)) != EOF && c != ';' && c != PARENS )
- line[line_len++] = c;
- if( c == EOF )
- break;
- if( c == ';' ) /* bypass externals representing data items */
- continue; /* now we know line ended with PARENS */
- line[ line_len ] = 0; /* terminate line */
-
- /*
- At this point, line either contains a function definition
- or a function type declaration or something else, perhaps
- an occurrence of parentheses in a data item definition. We
- only want function definitions.
- */
-
- /* extract the function name from this possible */
- /* function definition, and save it in fn_name. */
- strcpy( fn_name, get_fn_name( line ) );
- definition_line_num = line_num;
-
- /* exclude the case of parenthetical expressions */
- /* in a data definition, e.g. within array brackets */
- if( !fn_name[0] )
- continue;
-
- /* skip white space */
- while( (c = filter_data(fp_source)) != EOF && isspace(c) )
- ; /* EMPTY */
-
- if( c == ';' || c == ',' ) /* functions type check declaration, */
- continue; /* so bypass it */
-
- if( c == PARENS ) /* something pointing to a function */
- continue; /* bypass it (could be a mistake - if */
- /* it's a function returning a pointer */
- /* to a function we'll miss it) */
-
- if( c == EOF )
- break;
-
-
- /* skip any parameter declarations - */
- /* eat until braces or EOF */
- while( c != BRACES && c != EOF )
- c = filter_data(fp_source);
-
- /* append this function definition */
- /* to the output table */
- #ifdef PRINTING_LINE_NUMBER
- fprintf( fp_out, "\n\t%-25s", fn_name );
- fprintf( fp_out, "%4d", definition_line_num );
- #else
- fprintf( fp_out, "\n\t%s", fn_name );
- #endif
- got_fn_defn = TRUE;
- }
- fclose(fp_source);
- /* got_fn_defn FALSE if no functions in file */
- /* write file terminator */
- fputs( got_fn_defn ? ";\n" : "\n\t;\n", fp_out );
- return OK;
- }
-
- /*
- assuming that the input line ends with a function name,
- extract and return this name (as a pointer into the input line).
- */
- char *
- get_fn_name(line)
- char *line;
- {
- char *name_ptr;
- int len;
-
- if( !(len = strlen(line)) )
- return line;
-
- name_ptr = line + len - 1;
-
- while( isspace(*name_ptr) ) /* skip trailing white space */
- name_ptr--;
- *(name_ptr + 1) = 0; /* terminate fn name */
-
- /* function names consist entirely of */
- /* alphanumeric chars and underscores */
- while( (isalnum(*name_ptr) || *name_ptr == '_') && name_ptr >= line )
- name_ptr--;
- /* if this alleged function name begins */
- /* with a digit, return an empty string */
- if( isdigit(*++name_ptr) )
- return( name_ptr + strlen(name_ptr) );
- return name_ptr; /* else return the function name */
- }
-
- /*
- Using the stream returned by filter_parens() as input, return a
- character stream in which any data initialization expressions
- between an equals sign and a semicolon have been replaced by a
- single semicolon. This will filter out anything involving
- parentheses in a data initialization expression, which might
- otherwise be mistaken for a function definition.
- */
- int filter_data(fp_source)
- FILE *fp_source;
- {
- int c;
-
- if( (c = filter_parens(fp_source)) != '=' )
- return c;
- while( (c = filter_parens(fp_source)) != ';' && c != EOF )
- ;
- return c;
- }
-
- /*
- Using the stream returned by filter_curly_braces() as input, return
- a character stream in which all characters between '(' and the
- matching ')' have been replaced by the single special value PARENS
- (any nested parentheses within this top level pair will also have
- been eaten). This will filter out anything within the parentheses
- delimiting the arguments in a function definition.
- */
- int
- filter_parens(fp_source)
- FILE *fp_source;
- {
- int paren_cnt, c;
-
- if( (c = filter_curly_braces(fp_source)) != '(' )
- return c;
- paren_cnt = 1;
- while( paren_cnt )
- {switch( filter_curly_braces(fp_source) )
- {
- case ')':
- paren_cnt--;
- break;
- case '(':
- paren_cnt++;
- break;
- case EOF:
- return EOF;
- }
- }
- return PARENS;
- }
-
- /*
- Using the stream returned by filter_ppdir() as input, return a
- character stream in which all characters between '{' and the
- matching '}' have been replaced by the single special value BRACES
- (any nested braces within this top level pair will also have been
- eaten). This will filter out anything internal to a function.
- */
- int
- filter_curly_braces(fp_source)
- FILE *fp_source;
- {
- int brace_cnt, c;
-
- if( (c = filter_ppdir(fp_source)) != '{' )
- return c;
- brace_cnt = 1;
- while( brace_cnt ) /* wait for brace count to return to zero */
- {switch( filter_ppdir(fp_source) )
- {
- case '}':
- brace_cnt--; /* subtract right braces */
- break;
- case '{':
- brace_cnt++; /* add left braces */
- break;
- case EOF:
- return EOF;
- }
- }
- return BRACES; /* brace count is now zero */
- }
-
- #define MAXLINE 1024
-
- /*
- Using the stream returned by filter_quotes() as input, return a
- character stream in which all preprocessor directives have been
- eaten.
- */
- int
- filter_ppdir(fp_source)
- FILE *fp_source;
- {
- int c, i;
- char line[MAXLINE + 1];
-
- while(TRUE)
- { /* does this line begin a preprocessor directive? */
- if( (c = filter_quotes(fp_source)) != '#' )
- return c; /* no, return character */
- /* yes, store until newline or EOF */
- if( (c = get_ppdir_line( fp_source, line )) == EOF )
- return EOF;
- if( strncmp( line, "#define", 6) ) /* if not #define directive */
- continue;
- if( line[ strlen(line) - 1 ] != '\\' ) /* if #define ends with "\" */
- continue;
- else
- while(TRUE) /* keep eating lines */
- {
- if( (c = get_ppdir_line( fp_source, line )) == EOF )
- return EOF;
- /* done with this #define directive if this
- line is not also a continueation line */
- if( line[ strlen(line) - 1 ] != '\\' )
- break;
- }
- }
- }
-
- /*
- Utility routine used by filter_ppdir() -
- read the character stream using filter_quotes, storing characters
- int he parameter "line", until EOF or '\n' is encountered. Return
- EOF or '\n' accordingly.
- */
- int
- get_ppdir_line( fp_source, line )
- FILE *fp_source;
- char *line;
- {
- int i, c;
-
- /* store until newline or EOF */
- for( i=0; i<MAXLINE && (c = filter_quotes(fp_source)) != '\n'
- && c != EOF; i++)
- line[i] = c;
- line[i] = 0; /* terminate string */
- if( c == EOF )
- return EOF;
- return '\n';
- }
-
- /*
- Using the stream returned by filter_cmt() as input, return a
- character stream in which any quoted character or quoted string has
- been collapsed to the single special value QUOTES to avoid
- considering special characters like '{', '}', '(', or ')' which may
- occur within quotes.
- */
- int
- filter_quotes(fp_source)
- FILE *fp_source;
- {
- int c1, c2;
-
- if( (c1 = filter_cmt(fp_source)) != '\'' && c1 != '"' )
- return c1; /* pass char through if not a single or double quote */
- while( TRUE )
- {
- switch( c2 = filter_cmt(fp_source) )
- {
- case '\\': /* beginning of escape sequence */
- filter_cmt(fp_source); /* so eat next char */
- break;
- case EOF:
- return EOF;
- default:
- if( c2 == c1 ) /* found end of quoted char or string */
- return QUOTES;
- }
- }
- }
-
- /*
- return character stream, eating comments.
- Returns EOF if end of file.
- nested comments are allowed.
- */
- int
- filter_cmt(fp_source)
- FILE *fp_source;
- {
- /* values for state */
- #define STABLE 0 /* not in process of changing the comment
- level: i.e., not in the middle of a
- slash-star or star-slash combination */
- #define IN_CMT_FS 1 /* got '/', looking for '*' */
- #define OUT_CMT_STAR 2 /* got '*', looking for '/' */
-
- int c, state=STABLE, cmt_level = 0;
-
- while( TRUE )
- {
- c = fgetc(fp_source);
-
- if( c == '\n') line_num++;
-
- else if( c == EOF )
- return EOF;
-
- switch(state)
- {
- case STABLE:
- if( c == '*' )
- state = OUT_CMT_STAR;
- else if( c == '/' )
- state = IN_CMT_FS;
- break;
-
- case IN_CMT_FS:
- if( c == '*' )
- {
- state = STABLE;
- #ifdef NESTED_COMMENTS
- cmt_level++; /* descend one comment level */
- #else
- cmt_level = 1; /* descend to comment level */
- #endif
- continue;
- }
- else if( !cmt_level ) /* if '/' not followed by '*'
- and outside any comment */
- {ungetc( c, fp_source ); /* push back this char
- and return the '/' */
- return '/';
- }
- else if( c != '/' ) /* stay in state IN_CMT_FS */
- state = STABLE; /* if next char is '/' as well */
- break;
-
- case OUT_CMT_STAR:
- if( c == '/' )
- {
- cmt_level--; /* ascend one comment level */
- state = STABLE;
- continue;
- }
- else if( ! cmt_level ) /* if '*' not followed by '/' */
- { /* and outside any comment */
- ungetc( c, fp_source ); /* push back this char */
- return '*'; /* and return the '*' */
- }
- else if( c != '*' ) /* stay in state IN_CMT_FS */
- state = STABLE; /* if next char is '*' as well */
- break;
- }
-
- if( state == STABLE && !cmt_level ) /* if outside any comment */
- return c; /* return character */
- }
- }
-
-
- /*
-
- ------------------------ debugging code --------------------------------
- #define RR(x) if(debugging) printf x
- int debugging=1;
- #define filter_data(fp) copy(filter_data(fp))
- #undef filter_data
- FILE *dfile;
- dfile=fopen( "bld.out", "w" );
- fclose(dfile);
-
- char *copy(s) char *s;
- { extern FILE *dfile;
- fputs(s, dfile);
- fputc('\n', dfile);
- return s;
- }
-
- int copy(c) int c;
- { extern FILE *dfile;
- if( c == EOF) fputs( "EOF", dfile );
- else if( c == BRACES) fputs( "BRACES", dfile );
- else if( c == PARENS) fputs( "PARENS", dfile );
- else if( c == QUOTES) fputs( "QUOTES", dfile );
- else fputc(c, dfile);
- return c;
- }
-
- int stdcopy(c) int c;
- {
- if( c == EOF) fputs( "EOF", stdout );
- else if( c == BRACES) fputs( "BRACES", stdout );
- else if( c == PARENS) fputs( "PARENS", stdout );
- else if( c == QUOTES) fputs( "QUOTES", stdout );
- else if( c == '\n') fputs( "\\n", stdout );
- else fputc(c, stdout);
- return c;
- }
-
- */
-
- help()
- {
- printf( "usage: bldfuncs [options] foo.c [bar.c ...]\n" );
- printf( " or: bldfuncs [options] *.c\n" );
- printf( "options...\n" );
- printf( " -a append to output file rather than replacing it\n");
- exit(1);
- }
-